{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <font color=Crimson size=6 face=\"宋体\" align=\"center\">二维平面坐标单层感知器线性划分</font>     \n",
    "--------------\n",
    "\n",
    "<font color=black size=4 face=\"微软雅黑\" align=\"center\">单层感知器模型如下图:</font> \n",
    "\n",
    "<img src=\"./perceptron.png\" align=\"left\">       \n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " <font color=black size=4 face=\"微软雅黑\" align=\"left\">人工神经元是一个多输入、单输出的非线性元件，其输入与输出关系可以表示为:</font> \n",
    "\n",
    "<font color=#0000FF size=4 face=\"微软雅黑\" align=\"center\">$$y=f(\\sum_{i=1}^{n}\\omega_i*x_i+b)$$</font>   \n",
    "\n",
    "<font color=#purple size=4 face=\"微软雅黑\" align=\"left\">其中$x_1,x_2,\\cdots,x_n$为训练样本的n个属性，$\\omega_1,\\omega_2，\\cdots,\\omega_n$为针对每一个属性的权重，b为偏置信号，f为激活函数</font>     \n",
    "\n",
    "\n",
    "\n",
    "------------"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---------\n",
    "*********\n",
    "# 接下来就以2维平面坐标为例，使用单层感知器进行分类\n",
    " ## 1.首先导入输入训练样本:10个2维坐标\n",
    " "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练样本矩阵如下：\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([[ 1.,  4.,  1.],\n",
       "       [ 3.,  5.,  1.],\n",
       "       [ 8., 10.,  1.],\n",
       "       [ 2.,  8.,  1.],\n",
       "       [ 4.,  9.,  1.],\n",
       "       [ 7.,  6.,  1.],\n",
       "       [ 1.,  1.,  1.],\n",
       "       [ 7.,  5.,  1.],\n",
       "       [ 2.,  2.,  1.],\n",
       "       [ 3.,  4.,  1.]])"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "#定义坐标,设定10组输入数据，每组为（x,y）\n",
    "Training_Input=np.array([[1,4],\n",
    "            [3,5],\n",
    "            [8,10],    \n",
    "            [2,8],            \n",
    "            [4,9],\n",
    "            [7,6],            \n",
    "            [1,1],  \n",
    "            [7,5],\n",
    "            [2,2],\n",
    "            [3,4]]);\n",
    "new_col=np.ones((1,len(Training_Input)))      #生成一列偏置信号全为1     \n",
    "Training_Input= np.column_stack((Training_Input,new_col.T))      #将偏置信号加入训练样本中\n",
    "print(\"训练样本矩阵如下：\")\n",
    "Training_Input"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  \n",
    "  ## 2.针对每一个训练样本都给出它的期望输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "输入的训练样本集如下图所示：\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "#设定输入向量的期待输出值\n",
    "Target_Output=np.array([1,1,1,1,1,-1,-1,-1,-1,-1]);\n",
    "for i in range(len(Training_Input)):\n",
    "    if(Target_Output[i]==1):\n",
    "        plt.plot(Training_Input[i,0],Training_Input[i,1],'bo')\n",
    "    else:\n",
    "        plt.plot(Training_Input[i,0],Training_Input[i,1],'ro')\n",
    "\n",
    "print(\"输入的训练样本集如下图所示：\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " \n",
    "  \n",
    "  ## 3.在迭代之前，对权重初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始值：       w0          w1           w2\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([ 0.94367633, -0.30905472, -0.75385746])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#设定权值向量(w0,w1,w2),权值范围为-1,1\n",
    "weights = (np.random.random(3)-0.5)*2;\n",
    "weights_initial=weights \n",
    "print(\"初始值：       w0          w1           w2\")\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4.设定学习率，记录感知器的迭代次数，初始化感知器的输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#设定学习率\n",
    "learning_rate = 0.3;\n",
    "#计算迭代次数\n",
    "iterative_num=0;\n",
    "#神经网络输出\n",
    "Output=0;"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.对感知器进行训练\n",
    "  ### 在这个过程激活函数采用的是阶跃函数，表达式为：\n",
    "$$f(x)=\\begin{cases}\n",
    "1 & (x\\geq0) \\\\\n",
    "0 & (x<0)\n",
    "\\end{cases}$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 每一次迭代后，根据感知器的输出调整各权重($\\omega_1,\\omega_2，\\cdots,\\omega_n$)的大小   \n",
    "   \n",
    "$$\\Delta\\omega=(y'-y)*\\eta*x$$\n",
    " \n",
    "$$\\omega_(new)=\\omega_(old)+\\Delta\\omega$$   \n",
    " \n",
    "  ### 其中y'为期望输出，y为当前迭代感知器的输出，$\\eta$为学习率"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def  update():\n",
    "    global  Training_Input,Target_Output,weights,learning_rate,iterative_num;   #引用的全局变量\n",
    "    iterative_num=iterative_num+1;      #每迭代一次，迭代次数+1\n",
    "    f_out=np.sign(np.dot(Training_Input,weights.T));    #通过阶跃函数处理后作为感知器的输出\n",
    "    #计算权值差\n",
    "    W_Tmp = learning_rate*((Target_Output-f_out.T).dot(Training_Input));\n",
    "    weights = weights+W_Tmp;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练完成！\n",
      "epoch: 42\n"
     ]
    }
   ],
   "source": [
    "weights_stored=weights\n",
    "MAX_TRAINING=5000     #设置最大的训练次数，超过这个次数就停止训练\n",
    "linear_separable=True\n",
    "while(1):\n",
    "    update()\n",
    "    Output=np.sign(np.dot(Training_Input,weights.T))\n",
    "    weights_stored=np.row_stack((weights_stored,weights))      #将每一次迭代后weight的值保存起来，一行一行地加入weights_stored矩阵\n",
    "    if(Output==Target_Output).all():\n",
    "        print('训练完成！')\n",
    "        print('epoch:',iterative_num)\n",
    "        linear_separable=True\n",
    "        break\n",
    "    if(iterative_num>MAX_TRAINING):\n",
    "        print(\"输入的训练样本集好像不是线性可分的，我已训练\"+str(MAX_TRAINING)+\"次了！  不过再训练更多次也许能够做到哦！\")\n",
    "        linear_separable=False\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.展示训练结果（通过移动滑条来查看不同迭代次数得到的分类直线）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "f3aaaf1411d542ae9d8489dfbdb48866",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "interactive(children=(IntSlider(value=21, description='Iterative_times', max=42), Output()), _dom_classes=('wi…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from pylab import *                                            #导入中文包\n",
    "mpl.rcParams['font.sans-serif'] = ['SimHei']                   #设置中文字体，如果不需要在图上显示中文，这两行大可不必要\n",
    "from ipywidgets import interact, interactive, fixed, interact_manual\n",
    "@interact(Iterative_times=(0,len(weights_stored)-1,1))\n",
    "def update_show_2(Iterative_times):\n",
    "   #分类直线y=kx+b\n",
    "    k=-weights_stored[Iterative_times,0]/weights_stored[Iterative_times,1]     #计算分类直线的k\n",
    "    d=-weights_stored[Iterative_times,2]/weights_stored[Iterative_times,1]     #计算分类直线的b\n",
    "    xdata=np.linspace(0,10)\n",
    "    plt.figure()\n",
    "    \n",
    "    for i in range(len(Training_Input)):                       #在图上画出训练样本点\n",
    "        if(Target_Output[i]==1):\n",
    "            plt.plot(Training_Input[i,0],Training_Input[i,1],'bo')\n",
    "        else:\n",
    "            plt.plot(Training_Input[i,0],Training_Input[i,1],'ro')\n",
    "\n",
    "    plt.plot(xdata,xdata*k+d,'g')                              #在图上画出分类直线\n",
    "    plt.title('训练'+str(Iterative_times)+'次的分类直线')      #给图增加标题\n",
    "    plt.show()\n",
    "    \n",
    "    print(\"   w0          w1           w2\")             \n",
    "    print(weights_stored[Iterative_times,0:])                 #显示此时的权值\n",
    "    #判断滑条滑至最右端的情况\n",
    "    if(Iterative_times==len(weights_stored)-1 and linear_separable==True):\n",
    "        print(\"感知器训练已结束，权值将不会再发生改变！！！\")\n",
    "    elif(Iterative_times==len(weights_stored)-1 and linear_separable==False):\n",
    "        print(\"它们似乎不是线性可分的\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
